<template>
  <!-- 平行光 -->
  <!-- 平行光是沿着特定方向发射的光。这种光的表现像是无限远,从它发出的光线都是平行的。常常用平行光来模拟太阳光 的效果; 太阳足够远，因此我们可以认为太阳的位置是无限远，所以我们认为从太阳发出的光线也都是平行的。 -->
  <div class="directional-light">
    <div id="scene"></div>
    <div class="controls-box">
      <section>
        <el-row>
          <el-checkbox v-model="lightProperties.visible"
            >是否展示方向光</el-checkbox
          >
        </el-row>
        <el-row>
          <el-col :span="8" class="label-col"
            ><label> 方向光颜色</label></el-col
          >
          <el-col :span="16">
            <div class="color-select-layer">
              <el-input :value="lightProperties.color"></el-input>
              <div>
                <el-color-picker
                  v-model="lightProperties.color"
                ></el-color-picker>
              </div>
            </div>
          </el-col>
        </el-row>
        <el-row>
          <div v-for="(item, key) in lightProperties" :key="key">
            <div v-if="item && item.name != undefined">
              <el-col :span="8">
                <span class="vertice-span">{{ item.name }}</span>
              </el-col>
              <el-col :span="13">
                <el-slider
                  v-model="item.value"
                  :min="item.min"
                  :max="item.max"
                  :step="item.step"
                ></el-slider>
              </el-col>
              <el-col :span="3">
                <span class="vertice-span">{{ item.value }}</span>
              </el-col>
            </div>
          </div>
        </el-row>
        <el-row>
          <el-col :span="8" class="label-col"><label>target</label></el-col>
          <el-col :span="16">
            <el-select v-model="lightProperties.target" placeholder="请选择">
              <el-option
                v-for="item in options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              >
              </el-option>
            </el-select>
          </el-col>
        </el-row>
        <el-row>
          <el-checkbox v-model="lightProperties.castShadow"
            >是否产生阴影</el-checkbox
          >
        </el-row>
      </section>
    </div>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

let scene; //场景
let camera; //相机
let renderer; //创建渲染器
let controls; //控制器
export default {
  data() {
    return {
      options: [
        {
          value: "cube",
          label: "cube",
        },
        {
          value: "sphere",
          label: "sphere",
        },
        {
          value: "plane",
          label: "plane",
        },
      ],
      lightProperties: {
        positionX: {
          name: "positionX",
          value: 15,
          min: -40,
          max: 40,
          step: 1,
        },
        positionY: {
          name: "positionY",
          value: 40,
          min: -60,
          max: 100,
          step: 1,
        },
        positionZ: {
          name: "positionZ",
          value: 35,
          min: -40,
          max: 40,
          step: 1,
        },
        intensity: {
          name: "intensity",
          value: 3.1,
          min: 0,
          max: 5,
          step: 0.1,
        },

        color: "#fafafa",
        visible: true,
        target: "sphere",
        castShadow: true,
      },

      isShowColors: false,
      lightHelper: null,
      shadowCameraHelper: null,
      cube: null,
      sphere: null,
      plane: null,
      ambientLight: null,
      directionalLight: null,
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    // 初始化
    init() {
      this.createScene(); // 创建场景
      this.createMesh(); // 创建网格模型
      this.createCubeAndSphere(); // 创建方块和球
      this.createLight(); // 创建光源
      this.createCamera(); // 创建相机
      this.createRender(); // 创建渲染器
      this.createControls(); // 创建控件对象
      this.render(); // 渲染
    },
    // 创建场景
    createScene() {
      scene = new THREE.Scene();
    },
    // 创建网格模型
    createMesh() {
      const planeGeometry = new THREE.PlaneGeometry(200, 100); // 创建一个平面对象PlaneGeometry
      const planeMaterial = new THREE.MeshLambertMaterial({
        color: 0x409fee,
      }); // 材质对象Material
      this.plane = new THREE.Mesh(planeGeometry, planeMaterial);
      this.plane.receiveShadow = true;

      // 设置平面位置
      this.plane.rotation.x = -0.5 * Math.PI;
      this.plane.position.set(15, 0, 0);

      // 平面对象添加到场景中
      scene.add(this.plane);
    },
    // 创建方块和球
    createCubeAndSphere() {
      const geom = new THREE.BoxGeometry(4, 4, 4); // 创建几何对象geom
      const material = new THREE.MeshLambertMaterial({ color: 0xff0000 }); // 创建材质对象material
      this.cube = new THREE.Mesh(geom, material); // 创建网格对象cube
      this.cube.castShadow = true;
      this.cube.position.set(25, 6, 10);
      scene.add(this.cube); // 将网格对象添加到场景

      const sphereGeometry = new THREE.SphereGeometry(4, 26, 20); // 创建几何对象sphereGeometry
      const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0x7777ff }); // 创建材质对象sphereMaterial
      this.sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); // 创建网格对象sphere
      this.sphere.castShadow = true;
      this.sphere.position.set(25, 5, -5);
      scene.add(this.sphere); // 将网格对象添加到场景
    },

    // 创建光源
    createLight() {
      // 环境光
      this.ambientLight = new THREE.AmbientLight(0xffffff, 0.1); // 创建环境光
      scene.add(this.ambientLight); // 将环境光添加到场景

      this.directionalLight = new THREE.DirectionalLight(0xffffff); // 创建方向光
      this.directionalLight.position.set(15, 40, 35); // 设置方向光源位置

      this.directionalLight.castShadow = true; //是否产生阴影
      // shadow.mapSize.width 和 shadow.mapSize.height  阴影映射宽度和阴影映射高度。决定了有多少像素用来生成阴影。当阴影具有锯齿状边缘或看起来不光滑时，可以增加这个值。在场景渲染之后无法更改，默认值都为512
      this.directionalLight.shadow.mapSize.width = 1024;
      this.directionalLight.shadow.mapSize.height = 1024;
      // 表示距离光源的哪一个位置开始生成阴影
      this.directionalLight.shadow.camera.near = 2;
      // 表示到距离光源的哪一个位置可以生成阴影
      this.directionalLight.shadow.camera.far = 100;

      // 方向光投影边界更新
      this.directionalLight.shadow.camera.top = 8;
      this.directionalLight.shadow.camera.bottom = -8;
      this.directionalLight.shadow.camera.left = -8;
      this.directionalLight.shadow.camera.right = 8;

      scene.add(this.directionalLight);

      // 创建辅助工具
      this.lightHelper = new THREE.DirectionalLightHelper(
        this.directionalLight
      );
      scene.add(this.lightHelper);

      scene.add(new THREE.AxesHelper(20));
    },
    // 创建相机
    createCamera() {
      const element = document.getElementById("scene");
      const width = element.clientWidth; // 窗口宽度
      const height = element.clientHeight; // 窗口高度
      const k = width / height; // 窗口宽高比
      camera = new THREE.PerspectiveCamera(35, k, 0.1, 1000);
      camera.position.set(165, 38, -10); // 设置相机位置

      camera.lookAt(new THREE.Vector3(10, 0, 0)); // 设置相机方向
      scene.add(camera);
    },
    // 创建渲染器
    createRender() {
      const element = document.getElementById("scene");
      renderer = new THREE.WebGLRenderer();
      renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
      renderer.shadowMap.enabled = true; // 显示阴影
      renderer.setClearColor(0x3f3f3f, 1); // 设置背景颜色
      element.appendChild(renderer.domElement);
    },
    // 更新属性
    updateFun() {
      // 方向光颜色更新
      this.directionalLight.color = new THREE.Color(this.lightProperties.color);
      // 方向光强度更新
      this.directionalLight.intensity = this.lightProperties.intensity.value;

      // 方向光位置更新
      this.directionalLight.position.set(
        this.lightProperties.positionX.value,
        this.lightProperties.positionY.value,
        this.lightProperties.positionZ.value
      );

      // 更新target
      if (this.lightProperties.target === "sphere") {
        this.directionalLight.target = this.sphere;
      } else if (this.lightProperties.target === "plane") {
        this.directionalLight.target = this.plane;
      } else {
        this.directionalLight.target = this.cube;
      }

      // 方向光是否可见更新
      this.directionalLight.visible = this.lightProperties.visible;
      // 是否产生阴影
      this.directionalLight.castShadow = this.lightProperties.castShadow;

      // 更新辅助工具
      this.lightHelper.update();
    },
    render() {
      this.updateFun();
      renderer.render(scene, camera);
      requestAnimationFrame(this.render);
    },
    // 创建控件对象
    createControls() {
      controls = new OrbitControls(camera, renderer.domElement);
      // target:(Vector3)  控件的焦点，.object围绕它运行。它可以随时手动更新以更改控件的焦点。
      controls.target.copy(this.plane.position);
    },
  },
};
</script>

<style scoped>
.directional-light {
  width: 100%;
  height: 100%;
  position: relative;
}
#scene {
  width: 100%;
  height: 100%;
}
.controls-box {
  position: absolute;
  right: 5px;
  top: 5px;
  width: 300px;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #c3c3c3;
}
.label-col {
  padding: 8px 5px;
}
.color-select-layer {
  display: flex;
}
.vertice-span {
  line-height: 38px;
  padding: 0 2px 0 10px;
}
</style>

